<!DOCTYPE html>
<html lang="zh-cn">
<head prefix="og: http://ogp.me/ns#">
  <meta charset="utf-8">
  <title>服务器架构总览 | Screeps 中文文档</title>
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Canonical links -->
  <link rel="canonical" href="http://screeps-cn.github.io/architecture.html">
  <!-- Alternative links -->
  
    
      <link rel="alternative" hreflang="en" href="http://screeps-cn.github.io/architecture.html">
    
      <link rel="alternative" hreflang="zh-tw" href="http://screeps-cn.github.io/zh-tw/architecture.html">
    
      <link rel="alternative" hreflang="zh-cn" href="http://screeps-cn.github.io/zh-cn/architecture.html">
    
      <link rel="alternative" hreflang="ru" href="http://screeps-cn.github.io/ru/architecture.html">
    
      <link rel="alternative" hreflang="ko" href="http://screeps-cn.github.io/ko/architecture.html">
    
  
  <!-- Icon -->
  <link rel="apple-touch-icon" sizes="57x57" href="/icon/apple-touch-icon-57x57.png">
  <link rel="apple-touch-icon" sizes="114x114" href="/icon/apple-touch-icon-114x114.png">
  <link rel="apple-touch-icon" sizes="72x72" href="/icon/apple-touch-icon-72x72.png">
  <link rel="apple-touch-icon" sizes="144x144" href="/icon/apple-touch-icon-144x144.png">
  <link rel="apple-touch-icon" sizes="60x60" href="/icon/apple-touch-icon-60x60.png">
  <link rel="apple-touch-icon" sizes="120x120" href="/icon/apple-touch-icon-120x120.png">
  <link rel="apple-touch-icon" sizes="76x76" href="/icon/apple-touch-icon-76x76.png">
  <link rel="apple-touch-icon" sizes="152x152" href="/icon/apple-touch-icon-152x152.png">
  <link rel="icon" type="image/png" href="/icon/favicon-196x196.png" sizes="196x196">
  <link rel="icon" type="image/png" href="/icon/favicon-160x160.png" sizes="160x160">
  <link rel="icon" type="image/png" href="/icon/favicon-96x96.png" sizes="96x96">
  <link rel="icon" type="image/png" href="/icon/favicon-16x16.png" sizes="16x16">
  <link rel="icon" type="image/png" href="/icon/favicon-32x32.png" sizes="32x32">
  <meta name="msapplication-TileColor" content="#2f83cd">
  <meta name="msapplication-TileImage" content="/icon/mstile-144x144.png">
  <!-- CSS -->
  <!-- build:css build/css/navy.css -->
  <link rel="stylesheet" href="/css/navy.css?1">
  <link rel="stylesheet" href="/css/prism.css">
  <!-- endbuild -->
  <link href="https://fonts.googleapis.com/css?family=Lato:300,400,700" rel="stylesheet" type="text/css">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css">
  <!-- RSS -->
  <link rel="alternate" href="/atom.xml" title="Screeps 中文文档">
  <!-- Open Graph -->
  <meta name="description" content="既然Screeps是一个为程序员设计的游戏, 你可能会对它服务器端怎么工作感兴趣. 对于我们来说, 我们也想把这个项目的一些高级架构透露出来.
重中之重
服务器端使用的软件有 Node.js 8.9.3, MongoDB 3, Redis 3.
我们在服务器端写了 20k 行的 JS 代码
运行时计算在 ovh 上的 40 个四核专用服务器上并行完成，使用 160 个 Intel Xeon CPU">
<meta property="og:type" content="website">
<meta property="og:title" content="服务器架构总览">
<meta property="og:url" content="http://screeps-cn.github.io/architecture.html">
<meta property="og:site_name" content="Screeps 中文文档">
<meta property="og:description" content="既然Screeps是一个为程序员设计的游戏, 你可能会对它服务器端怎么工作感兴趣. 对于我们来说, 我们也想把这个项目的一些高级架构透露出来.
重中之重
服务器端使用的软件有 Node.js 8.9.3, MongoDB 3, Redis 3.
我们在服务器端写了 20k 行的 JS 代码
运行时计算在 ovh 上的 40 个四核专用服务器上并行完成，使用 160 个 Intel Xeon CPU">
<meta property="og:image" content="http://screeps-cn.github.io/img/architecture_stage1.png">
<meta property="og:image" content="http://screeps-cn.github.io/img/architecture_stage2.png">
<meta property="og:image" content="http://screeps-cn.github.io/img/architecture_run.png">
<meta property="og:updated_time" content="2024-09-20T13:38:58.407Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="服务器架构总览">
<meta name="twitter:description" content="既然Screeps是一个为程序员设计的游戏, 你可能会对它服务器端怎么工作感兴趣. 对于我们来说, 我们也想把这个项目的一些高级架构透露出来.
重中之重
服务器端使用的软件有 Node.js 8.9.3, MongoDB 3, Redis 3.
我们在服务器端写了 20k 行的 JS 代码
运行时计算在 ovh 上的 40 个四核专用服务器上并行完成，使用 160 个 Intel Xeon CPU">
<meta name="twitter:image" content="http://screeps-cn.github.io/img/architecture_stage1.png">
  <!-- Google Analytics -->
  
</head>

<body>
  <div id="container">
    <header id="header" class="wrapper">
  <div id="header-inner" class="inner">
    <h1 id="logo-wrap">
      <a href="https://screeps.com" id="logo">Screeps</a>
      <a href="/index.html" id="logo-docs">docs</a>
    </h1>
    <a id="mobile-nav-toggle">
      <span class="mobile-nav-toggle-bar"></span>
      <span class="mobile-nav-toggle-bar"></span>
      <span class="mobile-nav-toggle-bar"></span>
    </a>
    <div id="header-main"></div>
  </div>
</header>

    <div id="content-wrap">
  <div id="content" class="wrapper">
    <div id="content-inner">
      <article class="article-container" itemscope itemtype="http://schema.org/Article">
        <div class="article-inner">
          <div class="article">
            <div class="inner">
              <header class="article-header">
                <h1 class="article-title" itemprop="name" id="top">服务器架构总览</h1>
                <a href="https://github.com/screeps-cn/docs/edit/master/source/architecture.md" class="article-edit-link" title="改进本文"><i class="fa fa-pencil"></i></a>
              </header>
              <div class="article-content" itemprop="articleBody">
                
                <p>既然Screeps是一个为程序员设计的游戏, 你可能会对它服务器端怎么工作感兴趣. 对于我们来说, 我们也想把这个项目的一些高级架构透露出来.</p>
<h2 id="重中之重" class="article-heading"><a href="#重中之重" class="headerlink" title="重中之重"></a>重中之重<a class="article-anchor" href="#重中之重" aria-hidden="true"></a></h2><ul>
<li>服务器端使用的软件有 <a href="https://nodejs.org/en/" target="_blank" rel="external">Node.js</a> 8.9.3, <a href="http://mongodb.org" target="_blank" rel="external">MongoDB</a> 3, <a href="http://redis.io/" target="_blank" rel="external">Redis</a> 3.</li>
<li>我们在服务器端写了 20k 行的 JS 代码</li>
<li>运行时计算在 ovh 上的 40 个四核专用服务器上并行完成，使用 160 个 Intel Xeon CPU E3-1231 v3 处理器内核（具有相应数量的节点实例）。</li>
<li>每个 shard 的 MongoDB 数据库都在一台有 24 核心，128GRAM 的机器上运行，每秒处理 30k 的请求。</li>
<li>玩家的运行时代码是在内存中工作，不进行任何硬盘和数据库请求</li>
</ul>
<h2 id="架构总览" class="article-heading"><a href="#架构总览" class="headerlink" title="架构总览"></a>架构总览<a class="article-anchor" href="#架构总览" aria-hidden="true"></a></h2><p>所有的游戏数据都是储存在 MongoDB 里. 每一个游戏对象都是分散的数据库文档(表). 它解释了由数据库分配的<code>id</code>属性对象的特定视图。</p>
<p>每一个游戏中的 tick 都是被 Redis 上的特殊同步代码控制的. 一个 tick 有两个阶段:</p>
<ol>
<li><strong>阶段1：解析代码</strong></li>
<li><strong>阶段2：执行代码</strong></li>
</ol>
<p>下面是一张阶段处理流程图:</p>
<p><img src="img/architecture_stage1.png" alt=""></p>
<p><img src="img/architecture_stage2.png" alt=""></p>
<p>每一个阶段都会创建一个任务队列。第一阶段的任务是执行所有活跃玩家的脚本，而第二阶段是处理游戏世界的房间。队列存储为一个 redis 列表，每个任务都由单独的计算机单独处理。</p>
<p>每个 tick 开始时会生成一个列表，其中列出了所有活跃的玩家，这些玩家被放入队列中以处理他们的游戏脚本。所有服务器从队列接收任务，请求玩家需要的数据库数据，并启动他或她的游戏脚本的运算处理，收集各种游戏对象的命令。当队列净空后，第二阶段开始。所有活动的命令都被放入队列，运行时服务器开始处理每个房间中对象的命令。（通俗点讲，第一阶段只会把所有检测到的活跃玩家的代码解析出来，看看哪些需要在第二阶段执行，第二阶段去执行这些代码）</p>
<p>尽管第二阶段的不同房间和第一阶段的不同玩家被分别并行处理，但并行处理的数量严格和 cpu 的数量对应。一个房间和一个玩家由一个核心同步处理，排除了各种其他条件。</p>
<p>两个阶段完成后，就形成了一定数量的更改数据库中游戏对象的请求。 这些要求在第二阶段结束后<a href="https://docs.mongodb.org/manual/core/bulk-write-operations/" target="_blank" rel="external">在bulk里得到了执行</a>。 MongoDB 3 使用了新的储存引擎<a href="http://www.wiredtiger.com/" target="_blank" rel="external">WiredTiger</a> 根据文档级的并发性，它允许利用数据库服务器上多个并行线程的优势。 当整个数据库操作完成后, 整个系统开始执行下一个tick的操作.</p>
<p>数据库对象更新是唯一需要硬盘访问的操作。在数据库服务器上，磁盘刷新每分钟只执行一次，不会影响根本不使用磁盘的运行时服务器（它们上没有磁盘）。运行时服务器接收游戏对象和<code>内存</code>对象的就绪数据，这些数据甚至在任务启动之前就加载到RAM中。所有有用的工作都是由运行时服务器的cpu核心来完成的，这些cpu核心在tick的第一阶段即计算阶段被玩家“租用”。</p>
<h2 id="服务器扩展" class="article-heading"><a href="#服务器扩展" class="headerlink" title="服务器扩展"></a>服务器扩展<a class="article-anchor" href="#服务器扩展" aria-hidden="true"></a></h2><p>该系统的设计允许在两个级别上轻松扩展：</p>
<ul>
<li>当数据库上的负载增加时 (例如： 玩家们在 shard 上更加活跃), 我们可以选择增加 WiredTiger 的 CPU 数或增加更多的 shard (每一个 shard 都有自己的数据库).</li>
<li>当玩家计算所产生的 cpu 总负载的增加时，我们可以添加更多的运行时服务器来执行这些计算。在它们启动后的一分钟内，它们就可以从 redis 队列接收和处理任务。</li>
</ul>
<h2 id="玩家脚本运行环境" class="article-heading"><a href="#玩家脚本运行环境" class="headerlink" title="玩家脚本运行环境"></a>玩家脚本运行环境<a class="article-anchor" href="#玩家脚本运行环境" aria-hidden="true"></a></h2><p>Node.JS 的 <a href="https://nodejs.org/api/vm.html" target="_blank" rel="external"><code>vm</code></a> 库在计算游戏脚本阶段执行任务时使用. 每个节点实例进程都启动一个单独的 fork，该 fork 不能访问其父进程。在启动后，fork 会立即向数据库发出一个预先请求，以获取计算所需的数据。 然后它为用户创建一个 context 并执行 <a href="https://nodejs.org/api/vm.html#vm_vm_runincontext_code_contextifiedsandbox_options" target="_blank" rel="external"><code>vm.runInContext</code></a>. context 会保存在 fork 中以备将来使用，这允许您在脚本中重复使用 <code>global</code> 对象和 <code>require</code> 缓存。 而且，编译<a href="http://v8project.blogspot.com.by/2015/07/code-caching.html" target="_blank" rel="external">缓存脚本代码</a>可以加快之后的代码编译速度。</p>
<p><img src="img/architecture_run.png" alt=""></p>
<p>尽管 <code>runincontext</code> 是特定于每个玩家的代码执行超时才会被调用的，但它并不总是能够在某些工作负载类型下优雅地完成脚本执行。如果出现这种情况，则在超时时终止整个 fork 而不是 vm。这个过程中的所有玩家 context 都会消失，然后从头开始重新创建。</p>
<p>未来，我们计划开源所有服务器端系统的代码，让您在自己的机器上可以启动 Screeps 进行模拟并研究。</p>

              </div>
              <footer class="article-footer">
                <time class="article-footer-updated" datetime="2024-09-20T13:38:58.407Z" itemprop="dateModified">上次更新：9月 20, 2024</time>
                <a href="/cpu-limit.html" class="article-footer-prev"><i class="fa fa-chevron-left"></i><span>CPU 限制</span></a><a href="/ptr.html" class="article-footer-next"><span>公开测试区域 (PTR)</span><i class="fa fa-chevron-right"></i></a>
              </footer>
            </div>
          </div>
          <aside id="article-toc" role="navigation">
            
            <div id="article-toc-inner">
              <strong class="sidebar-title">目录</strong>
              <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#重中之重"><span class="toc-text">重中之重</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#架构总览"><span class="toc-text">架构总览</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#服务器扩展"><span class="toc-text">服务器扩展</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#玩家脚本运行环境"><span class="toc-text">玩家脚本运行环境</span></a></li></ol>
              <a href="#" id="article-toc-top">回到顶部</a>
            </div>
            
          </aside>
        </div>
      </article>
      <aside id="sidebar" role="navigation">
  <div class="inner"><a href="/api/" class=api-link><span>API Reference</span><img src="/img/link-external.svg"></a><a href="/index.html" class="sidebar-link">总览</a><strong class="sidebar-title">游戏环境</strong><a href="/introduction.html" class="sidebar-link">简介</a><a href="/creeps.html" class="sidebar-link">Creeps</a><a href="/control.html" class="sidebar-link">控制</a><a href="/defense.html" class="sidebar-link">防御</a><a href="/respawn.html" class="sidebar-link">重生</a><a href="/start-areas.html" class="sidebar-link">初始区域</a><a href="/resources.html" class="sidebar-link">资源</a><a href="/market.html" class="sidebar-link">市场</a><a href="/invaders.html" class="sidebar-link">NPC 入侵者</a><a href="/power.html" class="sidebar-link">超能</a><strong class="sidebar-title">脚本</strong><a href="/scripting-basics.html" class="sidebar-link">脚本基础</a><a href="/global-objects.html" class="sidebar-link">全局对象</a><a href="/modules.html" class="sidebar-link">模块</a><a href="/debugging.html" class="sidebar-link">调试</a><a href="/game-loop.html" class="sidebar-link">游戏循环</a><a href="/commit.html" class="sidebar-link">外部提交</a><a href="/simultaneous-actions.html" class="sidebar-link">同步操作</a><a href="/cpu-limit.html" class="sidebar-link">CPU 限制</a><strong class="sidebar-title">其他</strong><a href="/architecture.html" class="sidebar-link current">服务器架构</a><a href="/ptr.html" class="sidebar-link">公开测试区域 (PTR)</a><a href="/third-party.html" class="sidebar-link">第三方工具</a><a href="/auth-tokens.html" class="sidebar-link">验证令牌</a><a href="/community-servers.html" class="sidebar-link">社区服务器</a><a href="/tos.html" class="sidebar-link">服务条款</a><a href="/privacy-policy.html" class="sidebar-link">隐私政策</a><strong class="sidebar-title">资源</strong><a href="http://blog.screeps.com" class="sidebar-link">博客</a><a href="http://blog.screeps.com/categories/Changelogs/" class="sidebar-link">修改日志</a><a href="http://chat.screeps.com" class="sidebar-link">聊天室</a><a href="https://screeps.com/forum/" class="sidebar-link">论坛</a><strong class="sidebar-title">贡献文章</strong><a href="/contributed/rules.html" class="sidebar-link">贡献规则</a><a href="/contributed/advanced_grunt.html" class="sidebar-link">高级 Grunt 使用</a><a href="/contributed/modifying-prototypes.html" class="sidebar-link">修改原型</a><a href="/contributed/caching-overview.html" class="sidebar-link">缓存概述</a><a href="/contributed/ps_ubuntu.html" class="sidebar-link">私有服务器 MongoDB</a></div>
</aside>
    </div>
  </div>
</div>

    <footer id="footer" class="wrapper">
  <div class="inner">
    <div id="footer-copyright">
      &copy; 2024 <a href="https://screeps.com/" target="_blank">Screeps</a><br>
      Documentation licensed under <a href="http://creativecommons.org/licenses/by/4.0/" target="_blank">CC BY 4.0</a>.
    </div>
    <div id="footer-links">
      <a href="https://github.com/screeps-cn/docs" class="footer-link" target="_blank"><i class="fa fa-github-alt"></i></a>
    </div>
  </div>
</footer>

  </div>
  <div id="mobile-nav-dimmer"></div>
  <nav id="mobile-nav">
  <div id="mobile-nav-inner">
    
      <a href="/api/" class=api-link><span>API Reference</span><img src="/img/link-external.svg"></a><a href="/index.html" class="mobile-nav-link">总览</a><strong class="mobile-nav-title">游戏环境</strong><a href="/introduction.html" class="mobile-nav-link">简介</a><a href="/creeps.html" class="mobile-nav-link">Creeps</a><a href="/control.html" class="mobile-nav-link">控制</a><a href="/defense.html" class="mobile-nav-link">防御</a><a href="/respawn.html" class="mobile-nav-link">重生</a><a href="/start-areas.html" class="mobile-nav-link">初始区域</a><a href="/resources.html" class="mobile-nav-link">资源</a><a href="/market.html" class="mobile-nav-link">市场</a><a href="/invaders.html" class="mobile-nav-link">NPC 入侵者</a><a href="/power.html" class="mobile-nav-link">超能</a><strong class="mobile-nav-title">脚本</strong><a href="/scripting-basics.html" class="mobile-nav-link">脚本基础</a><a href="/global-objects.html" class="mobile-nav-link">全局对象</a><a href="/modules.html" class="mobile-nav-link">模块</a><a href="/debugging.html" class="mobile-nav-link">调试</a><a href="/game-loop.html" class="mobile-nav-link">游戏循环</a><a href="/commit.html" class="mobile-nav-link">外部提交</a><a href="/simultaneous-actions.html" class="mobile-nav-link">同步操作</a><a href="/cpu-limit.html" class="mobile-nav-link">CPU 限制</a><strong class="mobile-nav-title">其他</strong><a href="/architecture.html" class="mobile-nav-link current">服务器架构</a><a href="/ptr.html" class="mobile-nav-link">公开测试区域 (PTR)</a><a href="/third-party.html" class="mobile-nav-link">第三方工具</a><a href="/auth-tokens.html" class="mobile-nav-link">验证令牌</a><a href="/community-servers.html" class="mobile-nav-link">社区服务器</a><a href="/tos.html" class="mobile-nav-link">服务条款</a><a href="/privacy-policy.html" class="mobile-nav-link">隐私政策</a><strong class="mobile-nav-title">资源</strong><a href="http://blog.screeps.com" class="mobile-nav-link">博客</a><a href="http://blog.screeps.com/categories/Changelogs/" class="mobile-nav-link">修改日志</a><a href="http://chat.screeps.com" class="mobile-nav-link">聊天室</a><a href="https://screeps.com/forum/" class="mobile-nav-link">论坛</a><strong class="mobile-nav-title">贡献文章</strong><a href="/contributed/rules.html" class="mobile-nav-link">贡献规则</a><a href="/contributed/advanced_grunt.html" class="mobile-nav-link">高级 Grunt 使用</a><a href="/contributed/modifying-prototypes.html" class="mobile-nav-link">修改原型</a><a href="/contributed/caching-overview.html" class="mobile-nav-link">缓存概述</a><a href="/contributed/ps_ubuntu.html" class="mobile-nav-link">私有服务器 MongoDB</a>
    
  </div>
</nav>
  <!-- Scripts -->
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<!-- build:js build/js/main.js -->
<script src="/js/lang_select.js"></script>
<script src="/js/scrollingelement.js"></script>
<script src="/js/toc.js"></script>
<script src="/js/mobile_nav.js"></script>
<script src="/js/custom.js"></script>
<!-- endbuild -->
<script src="https://cdn.jsdelivr.net/retinajs/1.3.0/retina.min.js" async></script>

<!-- Algolia -->

</body>
</html>